package com.youqingkuang.ipmsg.file;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import com.youqingkuang.ipmsg.exception.DirectoryOutputException;
import com.youqingkuang.ipmsg.net.IPMSG;
import com.youqingkuang.ipmsg.net.IPTool;

public class DirectoryOutputStream extends OutputStream {

    protected int level = 0;

    protected File dir = null;

    protected FileOutputStream output = null;

    protected byte[] buffer = null;

    protected int buffered = 0;

    protected int headerSize = 0;

    protected int fileSize = 0;

    public DirectoryOutputStream(File dir) {
        this.dir = dir;
        buffer = new byte[IPMSG.MAX_SOCKBUF];

        if (dir.isFile()) {
            throw new IllegalArgumentException("argument 'dir' must be a directory");
        }
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

    public DirectoryOutputStream(String dir) {
        this(new File(dir));
    }

    @Override
    public void write(int b) throws IOException {
        buffer[buffered++] = (byte)b;
        if (buffered >= buffer.length >> 1) flush();
    }

    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }

    public void write(byte[] b, int off, int len) throws IOException {
        while (len > 0) {
            int writable = buffer.length - buffered;
            writable = writable < len ? writable : len;
            System.arraycopy(b, off, buffer, buffered, writable);
            buffered += writable;
            off += writable;
            len -= writable;
            if (buffered >= buffer.length >> 1) flush();
        }
    }

    public void flush() throws IOException {
        int writed = 0;
        while (writed < buffered) {
            if (headerSize == 0) {
                int cur = writed;
                for (; cur < buffered; ++cur) {
                    if (buffer[cur] == IPTool.separator) break;
                }
                if (buffer[cur] == IPTool.separator) {
                    headerSize = Integer.parseInt(new String(buffer, writed, cur - writed), 16);
                    if (writed + headerSize > buffered) {
                        headerSize = 0;
                        break;
                    } else {
                        parseHeader(writed, cur);
                        writed += headerSize;
                    }
                } else {
                    break;
                }
            } else {
                if (fileSize == 0) {
                    if (output != null) output.close();
                    headerSize = 0;
                } else {
                    int remain = buffered - writed;
                    remain = remain < fileSize ? remain : fileSize;
                    output.write(buffer, writed, remain);
                    writed += remain;
                    fileSize -= remain;
                }
            }
        }

        if (writed > 0) {
            buffered -= writed;
            if (buffered > 0) {
                System.arraycopy(buffer, writed, buffer, 0, buffered);
            }
        }
    }

    public void close() throws IOException {
        if (level > 0 && buffered > 0) flush();
        if (output != null) output.close();
    }

    protected void finalize() throws IOException {
        close();
    }

    protected void parseHeader(int writed, int cur) throws FileNotFoundException, DirectoryOutputException {
        int end = writed + headerSize;
        writed = ++cur;
        for (; cur < end; ++cur) {
            if (buffer[cur] == IPTool.separator) {
                if (cur + 1 >= end || buffer[cur + 1] != IPTool.separator) break;
                else ++cur;
            }
        }
        String filename = new String(buffer, writed, cur - writed);
        writed = ++cur;
        for (; cur < end; ++cur) {
            if (buffer[cur] == IPTool.separator) break;
        }
        fileSize = Integer.parseInt(new String(buffer, writed, cur - writed), 16);
        writed = ++cur;
        for (; cur < end; ++cur) {
            if (buffer[cur] == IPTool.separator) break;
        }
        int attr = Integer.parseInt(new String(buffer, writed, cur - writed), 16);

        if (IPMSG.GET_MODE(attr) == IPMSG.IPMSG_FILE_DIR) {
            ++level;
            fileSize = 0;
            if (dir.isDirectory()) {
                dir = new File(dir, filename);
            } else if (dir.isFile()) {
                dir = new File(dir.getParentFile(), filename);
            } else {
                throw new DirectoryOutputException();
            }
            dir.mkdir();
        } else if (IPMSG.GET_MODE(attr) == IPMSG.IPMSG_FILE_RETPARENT) {
            --level;
            fileSize = 0;
            if (dir.isDirectory()) {
                dir = dir.getParentFile();
            } else if (dir.isFile()) {
                dir = dir.getParentFile().getParentFile();
            } else {
                throw new DirectoryOutputException();
            }
            if (level == 0) {
                throw new DirectoryOutputException();
            }
        } else if (IPMSG.GET_MODE(attr) == IPMSG.IPMSG_FILE_REGULAR) {
            if (dir.isDirectory()) {
                dir = new File(dir, filename);
            } else if (dir.isFile()) {
                dir = new File(dir.getParentFile(), filename);
            } else {
                throw new DirectoryOutputException();
            }
            output = new FileOutputStream(dir);
        }
    }

}
